package com.yunzheng.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yunzheng.constants.SystemConstants;
import com.yunzheng.dto.*;
import com.yunzheng.entity.*;
import com.yunzheng.enums.ResultStatuesEnums;
import com.yunzheng.enums.RoleTypeEnums;
import com.yunzheng.exception.ServiceException;
import com.yunzheng.exception.SystemException;
import com.yunzheng.mapper.*;
import com.yunzheng.redis.RedisManager;
import com.yunzheng.util.HttpClientUtil;
import com.yunzheng.util.Md5Util;
import com.yunzheng.util.SnowFlakeUtil;
import com.yunzheng.vo.PuserShowVO;
import com.yunzheng.vo.UserShowVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunzheng.service.SysUserService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * @author 郭思良
 * @create 2020-07-23 15:38
 */

@Service
@Slf4j
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private SysUserMapper sysUserMapper;

    @Resource
    private SysAuthMapper sysAuthMapper;

    @Resource
    private RedisManager redisManager;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Resource
    private SysUserProjectRoleMapper sysUserProjectRoleMapper;

    @Override
    public UserShowVO selectInfoById(String userId) {
        // 查询该用户id的用户信息
        SysUser sysUser = sysUserMapper.selectById(userId);
        if (sysUser == null){
            throw new ServiceException(ResultStatuesEnums.USER_NOT_EXIST);
        }
        // 打包成VO返回
        UserShowVO showVO = new UserShowVO();
        BeanUtils.copyProperties(sysUser, showVO);
        log.debug("查询了用户" + userId + "的信息");
        return showVO;
    }

    @Override
    public void updateInfoById(String userId, UserChangeDTO userChangeDTO) {
        SysUser sysUser = new SysUser();
        BeanUtils.copyProperties(userChangeDTO, sysUser);
        sysUser.setUserId(userId);
        sysUserMapper.updateById(sysUser);
        log.debug("更新了用户 " + userId + " 的信息");
    }

    @Override
    public void removeUserById(String userId) {
        sysUserMapper.deleteById(userId);
        sysAuthMapper.deleteById(userId);
        sysUserProjectRoleMapper.delete(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_USER_ID, userId));
        log.debug("删除用户：" + userId);
    }

    @Override
    public void sentVerification(String userPhone) {
        //判断用户已经存在就没必要再发短信了
        ensureUserNotExist(userPhone);
        //发送并存储验证码
        sendSmsAndStore(userPhone);
    }

    private void sendSmsAndStore(String userPhone) {
        String response;
        try {
            response = HttpClientUtil.get("http://pay-api.xinjigame.com/htmlApi/htmlVerifyCode?phoneNum=" + userPhone);
            log.debug("给 {} 发送验证码", userPhone);
        } catch (IOException e) {
            throw new SystemException(e);
        }
        String code = response.substring(response.lastIndexOf(":") + 3, response.lastIndexOf("\""));
        log.debug("{} 的验证码为 {}", userPhone, code);
        redisManager.put(userPhone, code, SystemConstants.VERIFY_CODE_INVALID_SECONDS);
    }

    private void ensureUserNotExist(String userPhone) {
        SysAuth user = sysAuthMapper.selectOne(new QueryWrapper<SysAuth>().eq(SysAuth.COL_USER_PHONE, userPhone));
        // 用户已存在
        if (user != null) {
            throw new ServiceException(ResultStatuesEnums.PHONE_EXIST);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void register(RegisterDTO registerDTO) {
        //确保手机号没有被注册
        ensureUserNotExist(registerDTO.getUserPhone());
        //验证验证码
        ensureVerifyCode(registerDTO.getUserPhone(), registerDTO.getCaptcha());
        redisManager.delete(registerDTO.getUserPhone());
        //存入到sys_auth表
        String userId = saveToAuth(registerDTO.getUserPhone(),registerDTO.getPassword());
        //存入到sys_user表
        // TODO: 2020/8/16 saveToUser修改一下把team存入到数据库中
        saveToUser(registerDTO.getUserPhone(), registerDTO.getRealname(), userId);
        SysUserRole userRole = new SysUserRole(SnowFlakeUtil.nextId(), String.valueOf(RoleTypeEnums.NORMAL_USER.getRoleId()), userId);
        sysUserRoleMapper.insert(userRole);
        // 移除验证码
        redisManager.delete(registerDTO.getUserPhone());
        log.debug("成功注册用户：{}", userId);
    }


    private void saveToUser(String mobile, String realName, String userId) {
        //初始化基本信息
        SysUser user = new SysUser();
        user.setUserId(userId).setUserPhone(mobile).setRealname(realName).setAvatar("https://bkimg.cdn.bcebos.com/pic/2e2eb9389b504fc2403867d9ebdde71191ef6d83?x-bce-process=image/watermark,image_d2F0ZXIvYmFpa2U5Mg==,g_7,xp_5,yp_5")
                .setEmail("无").setLocation("无").setBirthday("1970-01-01").setTeam("无");
        sysUserMapper.insert(user);
        log.debug("{} 插入 sys_user 成功, {}", mobile, user.toString());
    }

    private String saveToAuth(String userPhone, String password) {
        //加密密码
        String salt = Md5Util.getSalt(SystemConstants.SALT);
        String newPassword = new Md5Hash(password,salt,SystemConstants.HASH_ITERATION).toHex();
        //生成用户的id
        String userId = SnowFlakeUtil.nextId();
        SysAuth user = new SysAuth();
        user.setUserPhone(userPhone).setPasswd(newPassword).setSalt(salt).setUserId(userId);
        //插入到sys_auth表中
        sysAuthMapper.insert(user);
        log.debug("{} 插入 sys_auth 成功, {}", userPhone, user.toString());

        return userId;
    }

    private void ensureVerifyCode(String userPhone, String captcha) {
        // 验证码错误，删除redis中的验证码
        String code = (String) redisManager.get(userPhone);
        log.debug("手机号：{} 对应的验证码为 {}，传入的验证码为 {}", userPhone, code, captcha);
        if (!captcha.equals(code)) {
            throw new ServiceException(ResultStatuesEnums.VERIFY_CODE_ERROR);
        }
    }

    private void ensureTwoPassword(String password, String passwordTwo) {
        if (!password.equals(passwordTwo)){
            throw new ServiceException(ResultStatuesEnums.PASSWORD_TWO_NOT_EQUALS);
        }
    }

    @Override
    public String login(String userPhone, String password) {
        /*
         * 这里首先要得到当前用户，然后进行login方法，从ShiroRealm接收到mobile
         */
        Subject currentUser = SecurityUtils.getSubject();
        //用数据库中的盐对前端传入的密码进行加密
        String salt = sysAuthMapper.selectOne(new QueryWrapper<SysAuth>().eq(SysAuth.COL_USER_PHONE,userPhone)).getSalt();
        if (salt == null){
            throw new ServiceException(ResultStatuesEnums.USER_NOT_EXIST);
        }
        String newPassword = new Md5Hash(password,salt,SystemConstants.HASH_ITERATION).toHex();
        try {
            //密码进行加密，将加密后的密码和数据库中的密码进行匹配
            currentUser.login(new UsernamePasswordToken(userPhone, newPassword));
        }catch (IncorrectCredentialsException e){
            // 转入shiroRealm中进行密码判断，密码错误抛出异常
            throw new ServiceException(ResultStatuesEnums.LOGIN_PASSWORD_ERROR);
        }
        log.debug("是否认证通过:" + currentUser.isAuthenticated());
        // 这里的 user 包含了 用户信息
        ActivityUser user = (ActivityUser) currentUser.getPrincipal();
        // 获得token
        String token = SnowFlakeUtil.nextId();
        // 在redis中存入token
        redisManager.put(token,String.valueOf(user.getUser().getUserId()));
        log.debug("{} 登录, token: {}", user.getUser().getUserPhone(), token);
        //返回token
        return token;
    }


    @Override
    public void outLogin(HttpServletRequest request) {
        redisManager.delete(request.getHeader(SystemConstants.TOKEN));
        SecurityUtils.getSubject().logout();
        log.debug("{} 退出", request.getHeader(SystemConstants.TOKEN));
    }

    @Override
    public void changePSW(String userId, String oldPasswd, String newPasswd) {
        //用户原来的盐、密码
        SysAuth user = sysAuthMapper.selectById(userId);
        String salt = user.getSalt();
        String passwd = user.getPasswd();
        //判断旧密码是否错误
        if (!passwd.equals(new Md5Hash(oldPasswd, salt, SystemConstants.HASH_ITERATION).toHex())){
            throw new ServiceException(ResultStatuesEnums.LOGIN_PASSWORD_ERROR);
        }
        //修改密码
        changePassword(newPasswd, userId);
        redisManager.delete(user.getUserPhone());
    }

    private void changePassword(String newPasswd, String userId) {
        SysAuth sysAuth = new SysAuth();
        String salt = Md5Util.getSalt(SystemConstants.SALT);
        sysAuth.setUserId(userId).setSalt(salt)
                .setPasswd(new Md5Hash(newPasswd, salt, SystemConstants.HASH_ITERATION).toHex());
        sysAuthMapper.updateById(sysAuth);
    }

//    @Override
//    public void changeVerification(String userId) {
//        // 获得当前用户的电话号码
//        String userPhone = sysUserMapper.selectById(userId).getUserPhone();
//        // 发送验证码
//        sendSmsAndStore(userPhone);
//    }

    @Override
    public void addProjectUser(String projectId, String userPhone) {
        String userId = sysUserMapper.selectOne(new QueryWrapper<SysUser>().eq(SysUser.COL_USER_PHONE, userPhone)).getUserId();
        if (userId == null){
            throw new ServiceException(ResultStatuesEnums.USER_NOT_EXIST);
        }
        // 判断该用户是否已经在该项目组中
        int row = sysUserProjectRoleMapper.selectCount(new QueryWrapper<SysUserProjectRole>()
                .eq(SysUserProjectRole.COL_USER_ID, userId).eq(SysUserProjectRole.COL_PROJECT_ID, projectId));
        if(row > 0){
            throw new ServiceException("该用户已经在该项目中");
        }
        SysUserProjectRole sysUserProjectRole = new SysUserProjectRole();
        sysUserProjectRole.setId(SnowFlakeUtil.nextId()).setProjectId(projectId).setRoleId(String.valueOf(RoleTypeEnums.NORMAL_USER.getRoleId()))
                .setUserId(userId);
        sysUserProjectRoleMapper.insert(sysUserProjectRole);
        log.debug("{} 项目添加普通成员 {}", projectId, userId);
    }

    @Override
    public List<PuserShowVO> showProjectUser(String projectId) {
        // 搜索到该项目组下的所有成员userId
        List<SysUserProjectRole> sysUserProjectRoles = sysUserProjectRoleMapper.selectList(new QueryWrapper<SysUserProjectRole>().eq(SysUserProjectRole.COL_PROJECT_ID, projectId));
        List<PuserShowVO> vos = new ArrayList<>();
        // 如果不为空，获取它们的具体用户信息包装成VO
        if (sysUserProjectRoles.size() > 0){
            for (SysUserProjectRole userProjectRole : sysUserProjectRoles){
                SysUser sysUser = sysUserMapper.selectById(userProjectRole.getUserId());
                PuserShowVO vo = new PuserShowVO();
                BeanUtils.copyProperties(sysUser, vo);
                vo.setRole(userProjectRole.getRoleId());
                vos.add(vo);
            }
        }
        return vos;
    }

    @Override
    public void forgetVerification(String userPhone) {
        //判断用户是否存在，如果不存在就不要发验证码了
        int rows = sysAuthMapper.selectCount(new QueryWrapper<SysAuth>().eq(SysAuth.COL_USER_PHONE, userPhone));
        if (rows == 0){
            throw new ServiceException(ResultStatuesEnums.USER_NOT_EXIST);
        }
        //发送验证码
        sendSmsAndStore(userPhone);
    }

    @Override
    public void forgetPSW(String userPhone, String newPasswd, String captcha, String passwdTwo) {
        //判断两次密码是否相同
        ensureTwoPassword(newPasswd, passwdTwo);
        //验证验证码
        ensureVerifyCode(userPhone, captcha);
        //修改密码
        SysAuth sysAuth = sysAuthMapper.selectOne(new QueryWrapper<SysAuth>().eq(SysAuth.COL_USER_PHONE, userPhone));
        changePassword(newPasswd, sysAuth.getUserId());
        redisManager.delete(userPhone);
    }
}

